package com.ssy.lingxi.order.serviceimpl.web;

import com.querydsl.jpa.impl.JPAQueryFactory;
import com.ssy.lingxi.common.constant.basic.EnableDisableStatus;
import com.ssy.lingxi.common.constant.basic.FundModeEnum;
import com.ssy.lingxi.common.constant.order.OrderPayChannelEnum;
import com.ssy.lingxi.common.constant.order.OrderPayTypeEnum;
import com.ssy.lingxi.common.constant.order.OrderPaymentParameterEnum;
import com.ssy.lingxi.common.exception.BusinessException;
import com.ssy.lingxi.common.model.dto.UserLoginCacheDTO;
import com.ssy.lingxi.common.response.ResponseCode;
import com.ssy.lingxi.common.response.Wrapper;
import com.ssy.lingxi.order.entity.*;
import com.ssy.lingxi.order.model.bo.PaymentParameterBO;
import com.ssy.lingxi.order.model.vo.basic.request.OrderPayChannelVO;
import com.ssy.lingxi.order.model.vo.payment.request.PaymentParameterChannelVO;
import com.ssy.lingxi.order.model.vo.payment.request.PaymentParameterInsertOrUpdateVO;
import com.ssy.lingxi.order.model.vo.payment.request.PaymentParameterPayTypeVO;
import com.ssy.lingxi.order.model.vo.payment.request.PaymentParameterVO;
import com.ssy.lingxi.order.model.vo.payment.response.PaymentParameterChannelDetailVO;
import com.ssy.lingxi.order.model.vo.payment.response.PaymentParameterDetailVO;
import com.ssy.lingxi.order.model.vo.payment.response.PaymentParameterItemVO;
import com.ssy.lingxi.order.model.vo.payment.response.PaymentParameterPayTypeDetailVO;
import com.ssy.lingxi.order.repository.MemberParameterRepository;
import com.ssy.lingxi.order.repository.MemberPaymentParameterRepository;
import com.ssy.lingxi.order.repository.MemberPaymentRepository;
import com.ssy.lingxi.order.service.base.IBaseCacheService;
import com.ssy.lingxi.order.service.feign.IPayFeignService;
import com.ssy.lingxi.order.service.web.IMemberPaymentParameterService;
import com.ssy.lingxi.order.utils.NumberUtil;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 支付能力 - 会员支付参数配置相关接口实现类
 * @author 万宁
 * @version 2.0.0
 * @date 2021-08-12
 */
@Service
public class MemberPaymentParameterServiceImpl implements IMemberPaymentParameterService {
    @Resource
    private IBaseCacheService baseCacheService;

    @Resource
    private IPayFeignService payFeignService;

    @Resource
    private MemberParameterRepository memberParameterRepository;

    @Resource
    private MemberPaymentRepository memberPaymentRepository;

    @Resource
    private MemberPaymentParameterRepository memberPaymentParameterRepository;

    @Resource
    private JPAQueryFactory jpaQueryFactory;

    /**
     * 查询支付参数配置详情
     *
     * @param headers HttpHeaders信息
     * @return 查询结果
     */
    @Override
    public Wrapper<List<PaymentParameterPayTypeDetailVO>> findMemberPaymentParameters(HttpHeaders headers) {
        UserLoginCacheDTO loginUser = baseCacheService.needLoginFromBusinessPlatform(headers);

        //Step 1: 从平台后台 - 会员支付策略中查
        QPlatformPaymentDO qPlatformPayment = QPlatformPaymentDO.platformPaymentDO;
        QPlatformPaymentMemberDO qPlatformPaymentMember = QPlatformPaymentMemberDO.platformPaymentMemberDO;
        QPlatformPaymentTypeDO qPlatformPaymentType = QPlatformPaymentTypeDO.platformPaymentTypeDO;
        QPlatformPaymentChannelDO qPlatformPaymentChannel = QPlatformPaymentChannelDO.platformPaymentChannelDO;
        List<PlatformPaymentTypeDO> platformPaymentTypes = jpaQueryFactory.selectDistinct(qPlatformPaymentType)
                .from(qPlatformPaymentType)
                .leftJoin(qPlatformPaymentChannel).on(qPlatformPaymentType.id.eq(qPlatformPaymentChannel.payType.id))
                .leftJoin(qPlatformPayment).on(qPlatformPaymentType.payment.id.eq(qPlatformPayment.id))
                .leftJoin(qPlatformPaymentMember).on(qPlatformPaymentMember.payment.id.eq(qPlatformPayment.id))
                .where(qPlatformPayment.status.eq(EnableDisableStatus.ENABLE.getCode()))
                .where(qPlatformPaymentType.fundMode.eq(FundModeEnum.DIRECT_TO_ACCOUNT.getCode()))
                .where(qPlatformPayment.allMembers.isTrue().or(qPlatformPaymentMember.memberId.eq(loginUser.getMemberId()).and(qPlatformPaymentMember.roleId.eq(loginUser.getMemberRoleId()))))
                .fetch();

        if(CollectionUtils.isEmpty(platformPaymentTypes)) {
            return Wrapper.success();
        }

        //Step 2:查询已有的配置
        List<MemberPaymentParameterDO> memberPaymentParameters = memberPaymentParameterRepository.findByMemberIdAndRoleId(loginUser.getMemberId(), loginUser.getMemberRoleId());

        //Step 3: 要隐藏的参数
        //List<Integer> hideParameterCodes = new ArrayList<>();
        List<Integer> hideParameterCodes = Stream.of(OrderPaymentParameterEnum.ALIPAY_APP_ID, OrderPaymentParameterEnum.ALIPAY_PUBLIC_KEY, OrderPaymentParameterEnum.APP_ALIPAY_PRIVATE_KEY,
                OrderPaymentParameterEnum.ALLIN_SYS_ID, OrderPaymentParameterEnum.ALLIN_PUBLIC_CERT_FILE, OrderPaymentParameterEnum.ALLIN_PRIVATE_CERT_FILE).map(OrderPaymentParameterEnum::getCode).collect(Collectors.toList());

        //Step 4: 拼接查询结果
        return Wrapper.success(platformPaymentTypes.stream().map(platformPaymentType -> {
            PaymentParameterPayTypeDetailVO detailVO = new PaymentParameterPayTypeDetailVO();
            detailVO.setPayType(platformPaymentType.getPayType());
            detailVO.setPayTypeName(OrderPayTypeEnum.getNameByCode(platformPaymentType.getPayType()));

            //已有的配置
            List<MemberPaymentParameterDO> paymentParameters = memberPaymentParameters.stream().filter(memberPaymentParameter -> memberPaymentParameter.getPayType().equals(platformPaymentType.getPayType())).collect(Collectors.toList());

            detailVO.setChannels(platformPaymentType.getPayChannels().stream().map(platformPaymentChannel -> {
                PaymentParameterChannelDetailVO channelDetailVO = new PaymentParameterChannelDetailVO();
                channelDetailVO.setPayChannel(platformPaymentChannel.getPayChannel());
                channelDetailVO.setPayChannelName(OrderPayChannelEnum.getNameByCode(platformPaymentChannel.getPayChannel()));
                //从现有的配置中查询参数值
                MemberPaymentParameterDO parameter = paymentParameters.stream().filter(memberPaymentParameter -> memberPaymentParameter.getPayChannel().equals(platformPaymentChannel.getPayChannel())).findFirst().orElse(null);
                channelDetailVO.setParameters(parameter == null || CollectionUtils.isEmpty(parameter.getParameters()) ? new ArrayList<>() : parameter.getParameters().stream().filter(p -> !hideParameterCodes.contains(p.getCode())).map(paymentParameterBO -> {
                    PaymentParameterDetailVO parameterDetailVO = new PaymentParameterDetailVO();
                    parameterDetailVO.setCode(paymentParameterBO.getCode());
                    parameterDetailVO.setKey(OrderPaymentParameterEnum.getKeyByCode(paymentParameterBO.getCode()));
                    parameterDetailVO.setValue(paymentParameterBO.getValue());
                    parameterDetailVO.setRemark(paymentParameterBO.getRemark());
                    return parameterDetailVO;
                }).collect(Collectors.toList()));
                return channelDetailVO;
            }).collect(Collectors.toList()));

            detailVO.setPayChannels(paymentParameters.stream().map(MemberPaymentParameterDO::getPayChannel).distinct().collect(Collectors.toList()));
            return detailVO;
        }).collect(Collectors.toList()));
    }

    /**
     * 根据支付渠道，查询支付参数下拉框列表
     *
     * @param headers   HttpHeaders信息
     * @param channelVO 接口参数
     * @return 查询结果
     */
    @Override
    public Wrapper<List<PaymentParameterItemVO>> findMemberPaymentPayChannelParameters(HttpHeaders headers, OrderPayChannelVO channelVO) {
        baseCacheService.needLoginFromBusinessPlatform(headers);
        //支付宝第三方授权码等，只能在平台后台 -> 商户支付参数中配置
        //通联支付所有参数，只能在平台后台 -> 平台支付参数中配置
        //建行B2B支付、数字人民币支付，只能在平台后台 -> 平台支付参数中配置
        List<Integer> ignorePayChannels = Stream.of(OrderPayChannelEnum.ALIPAY, OrderPayChannelEnum.ALLIN_WECHAT, OrderPayChannelEnum.ALLIN_ALI_PAY, OrderPayChannelEnum.ALLIN_QUICK, OrderPayChannelEnum.ALLIN_UNION, OrderPayChannelEnum.ALLIN_BALANCE, OrderPayChannelEnum.CCB_B2B, OrderPayChannelEnum.CCB_DIGITAL).map(OrderPayChannelEnum::getCode).collect(Collectors.toList());
        return ignorePayChannels.contains(channelVO.getPayChannel()) ? Wrapper.success(new ArrayList<>()) : Wrapper.success(Arrays.stream(OrderPaymentParameterEnum.values()).filter(e -> e.getPayChannels().stream().anyMatch(c -> c.getCode().equals(channelVO.getPayChannel())) && !e.getCode().equals(OrderPaymentParameterEnum.ALIPAY_THREAD_APP_AUTH_TOKEN.getCode())).sorted(Comparator.comparingInt(OrderPaymentParameterEnum::getCode)).map(e -> {
            PaymentParameterItemVO parameterVO = new PaymentParameterItemVO();
            parameterVO.setCode(e.getCode());
            parameterVO.setKey(e.getKey());
            return parameterVO;
        }).collect(Collectors.toList()));
    }

    /**
     * 校验前端传递的参数设置
     *
     * @param insertOrUpdateVO 接口参数
     * @return 校验结果
     */
    @Override
    public Wrapper<Void> checkMemberPaymentParameters(PaymentParameterInsertOrUpdateVO insertOrUpdateVO) {
        if(CollectionUtils.isEmpty(insertOrUpdateVO.getParameters())) {
            return Wrapper.success();
        }

        //校验授信支付的参数
        PaymentParameterPayTypeVO paymentParameterPayTypeVO = insertOrUpdateVO.getParameters().stream().filter(payType -> payType.getPayType().equals(OrderPayTypeEnum.CREDIT_LINE_PAYMENT.getCode())).findFirst().orElse(null);
        if(paymentParameterPayTypeVO == null) {
            return Wrapper.success();
        }

        List<PaymentParameterChannelVO> channels = paymentParameterPayTypeVO.getChannels();
        if(CollectionUtils.isEmpty(channels)) {
            return Wrapper.success();
        }

        PaymentParameterChannelVO channelVO = channels.stream().filter(channel -> channel.getPayChannel().equals(OrderPayChannelEnum.CREDIT.getCode())).findFirst().orElse(null);
        if(channelVO == null) {
            return Wrapper.success();
        }

        if(CollectionUtils.isEmpty(channelVO.getParameters())) {
            return Wrapper.success();
        }

        for (PaymentParameterVO parameter : channelVO.getParameters()) {
            String value = parameter.getValue();
            if(!StringUtils.hasLength(value)) {
                continue;
            }

            if(parameter.getCode().equals(OrderPaymentParameterEnum.CREDIT_APPLY_AMOUNT.getCode()) && NumberUtil.notDoubleOrNegative(value)) {
                return Wrapper.fail(ResponseCode.ORDER_CREDIT_AMOUNT_PARAMETER_SHOULD_MUST_GTE_ZERO);
            }

            if(parameter.getCode().equals(OrderPaymentParameterEnum.CREDIT_DAYS.getCode()) && NumberUtil.notLongOrNegative(value)) {
                return Wrapper.fail(ResponseCode.ORDER_CREDIT_DAYS_PARAMETER_SHOULD_MUST_GTE_ZERO);
            }

            if(parameter.getCode().equals(OrderPaymentParameterEnum.CREDIT_QUOTA_INCREAMENT.getCode()) && NumberUtil.notDoubleOrNegative(value)) {
                return Wrapper.fail(ResponseCode.ORDER_CREDIT_INCREMENT_RATE_PARAMETER_SHOULD_MUST_GTE_ZERO);
            }
        }

        return Wrapper.success();
    }

    /**
     * 新增或修改会员支付参数设置
     *
     * @param headers    HttpHeaders信息
     * @param parameterVO 接口参数
     * @return 操作结果
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public Wrapper<Void> createMemberPaymentParameters(HttpHeaders headers, PaymentParameterInsertOrUpdateVO parameterVO) {
        UserLoginCacheDTO loginUser = baseCacheService.needLoginFromBusinessPlatform(headers);

        //Step 1: 校验参数
        Wrapper<Void> checkResult = checkMemberPaymentParameters(parameterVO);
        if(checkResult.getCode() != ResponseCode.SUCCESS.getCode()) {
            return checkResult;
        }

        //Step 2: 删除已有的
        memberPaymentParameterRepository.deleteByMemberIdAndRoleId(loginUser.getMemberId(), loginUser.getMemberRoleId());

        //Step 3: 新增
        List<MemberPaymentParameterDO> paymentParameters = parameterVO.getParameters().stream().flatMap(parameter -> parameter.getChannels().stream().map(channel -> {
            MemberPaymentParameterDO memberPaymentParameter = new MemberPaymentParameterDO();
            memberPaymentParameter.setMemberId(loginUser.getMemberId());
            memberPaymentParameter.setRoleId(loginUser.getMemberRoleId());
            memberPaymentParameter.setPayType(parameter.getPayType());
            memberPaymentParameter.setPayTypeName(OrderPayTypeEnum.getNameByCode(parameter.getPayType()));
            memberPaymentParameter.setPayChannel(channel.getPayChannel());
            memberPaymentParameter.setPayChannelName(OrderPayChannelEnum.getNameByCode(channel.getPayChannel()));
            memberPaymentParameter.setParameters(CollectionUtils.isEmpty(channel.getParameters()) ? new ArrayList<>() : channel.getParameters().stream().map(p -> {
                PaymentParameterBO parameterBO = new PaymentParameterBO();
                parameterBO.setCode(p.getCode());
                parameterBO.setValue(p.getValue());
                parameterBO.setRemark(StringUtils.hasLength(p.getRemark()) ? p.getRemark().trim() : "");
                return parameterBO;
            }).collect(Collectors.toList()));
            return memberPaymentParameter;
        })).collect(Collectors.toList());

        //Step 4: 从会员通用支付参数配置中查询
        List<MemberParameterDO> memberParameters = memberParameterRepository.findAll();
        paymentParameters.forEach(parameter -> memberParameters.stream().filter(memberParameter -> memberParameter.getPayType().equals(parameter.getPayType()) && memberParameter.getPayChannel().equals(parameter.getPayChannel())).findFirst().ifPresent(memberParameter ->
                parameter.setParameters(insertOrUpdateParameter(parameter.getParameters(), memberParameter.getParameters()))));

        //Step 5: 从平台后台配置的会员支付参数配置中查询
        List<MemberPaymentDO> memberPayments = memberPaymentRepository.findByMemberIdAndRoleId(loginUser.getMemberId(), loginUser.getMemberRoleId());
        paymentParameters.forEach(parameter -> memberPayments.stream().filter(memberPayment -> memberPayment.getPayType().equals(parameter.getPayType()) && memberPayment.getPayChannel().equals(parameter.getPayChannel())).findFirst().ifPresent(memberPayment ->
                parameter.setParameters(insertOrUpdateParameter(parameter.getParameters(), memberPayment.getCode(), memberPayment.getValue(), memberPayment.getRemark()))));

        memberPaymentParameterRepository.saveAll(paymentParameters);

        //Step 6: 如果配置了“授信额度支付”，通知支付服务创建授信账户
        if(paymentParameters.stream().anyMatch(payment -> payment.getPayType().equals(OrderPayTypeEnum.CREDIT_LINE_PAYMENT.getCode()))) {
            payFeignService.notifyCreditAsync(loginUser.getMemberId(), loginUser.getMemberRoleId());
        }

        return Wrapper.success();
    }

    /**
     * 根据平台后台 - 通用商户参数配置，新增或修改所有会员支付参数
     *
     * @param payType           支付方式
     * @param payChannel        支付渠道
     * @param paymentParameters 支付参数列表
     * @return 操作结果
     */
    @Override
    public Wrapper<Void> insertOrUpdateMemberPaymentParameter(Integer payType, Integer payChannel, List<PaymentParameterBO> paymentParameters) {
        List<MemberPaymentParameterDO> parameters = memberPaymentParameterRepository.findByPayTypeAndPayChannel(payType, payChannel);
        if(CollectionUtils.isEmpty(parameters)) {
            return Wrapper.success();
        }

        parameters.forEach(parameter -> parameter.setParameters(insertOrUpdateParameter(parameter.getParameters(), paymentParameters)));
        memberPaymentParameterRepository.saveAll(parameters);
        return Wrapper.success();
    }

    /**
     * 根据平台后台 - 商户支付参数配置，新增或修改会员支付参数
     *
     * @param memberPayment 商户支付参数配置
     * @return 操作结果
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public Wrapper<Void> insertOrUpdateMemberPaymentParameter(MemberPaymentDO memberPayment) {
        MemberPaymentParameterDO memberPaymentParameter = memberPaymentParameterRepository.findFirstByMemberIdAndRoleIdAndPayTypeAndPayChannel(memberPayment.getMemberId(), memberPayment.getRoleId(), memberPayment.getPayType(), memberPayment.getPayChannel());
        if(memberPaymentParameter == null) {
            memberPaymentParameter = new MemberPaymentParameterDO();
            memberPaymentParameter.setMemberId(memberPayment.getMemberId());
            memberPaymentParameter.setRoleId(memberPayment.getRoleId());
            memberPaymentParameter.setPayType(memberPayment.getPayType());
            memberPaymentParameter.setPayTypeName(OrderPayTypeEnum.getNameByCode(memberPayment.getPayType()));
            memberPaymentParameter.setPayChannel(memberPayment.getPayChannel());
            memberPaymentParameter.setPayChannelName(OrderPayChannelEnum.getNameByCode(memberPayment.getPayChannel()));

            PaymentParameterBO parameterBO = new PaymentParameterBO();
            parameterBO.setCode(memberPayment.getCode());
            parameterBO.setValue(memberPayment.getValue());
            parameterBO.setRemark(memberPayment.getRemark());
            memberPaymentParameter.setParameters(Collections.singletonList(parameterBO));
        } else {
            memberPaymentParameter.setParameters(insertOrUpdateParameter(memberPaymentParameter.getParameters(), memberPayment.getCode(), memberPayment.getValue(), memberPayment.getRemark()));
        }

        memberPaymentParameterRepository.saveAndFlush(memberPaymentParameter);
        return Wrapper.success();
    }

    /**
     * 新增或更新PaymentParameterBO参数值
     * @param parameters 已经存在的参数列表
     * @param code 新增/更新参数的code
     * @param value 新增/更新的参数值
     * @param remark 新增/更新参数的备注说明
     * @return 参数列表
     */
    private List<PaymentParameterBO> insertOrUpdateParameter(List<PaymentParameterBO> parameters, Integer code, String value, String remark) {
        if(CollectionUtils.isEmpty(parameters)) {
            return Collections.singletonList(new PaymentParameterBO(code, value, remark));
        }

        PaymentParameterBO parameter = parameters.stream().filter(p -> p.getCode().equals(code)).findFirst().orElse(null);
        if(parameter == null) {
            parameters.add(new PaymentParameterBO(code, value, remark));
        } else {
            parameter.setValue(value);
            parameter.setRemark(remark);
        }

        return parameters;
    }

    /**
     * 新增或更新PaymentParameterBO参数值
     * @param parameters 已经存在的参数列表
     * @param values 新增/更新的参数值
     * @return 参数列表
     */
    private List<PaymentParameterBO> insertOrUpdateParameter(List<PaymentParameterBO> parameters, List<PaymentParameterBO> values) {
        if(CollectionUtils.isEmpty(parameters)) {
            return values;
        }

        parameters.forEach(parameter -> values.stream().filter(v -> v.getCode().equals(parameter.getCode())).findFirst().ifPresent(v -> {
            parameter.setValue(v.getValue());
            parameter.setRemark(v.getRemark());
        }));

        parameters.addAll(values.stream().filter(v -> parameters.stream().noneMatch(parameter -> parameter.getCode().equals(v.getCode()))).collect(Collectors.toList()));

        return parameters;
    }

    /**
     * 根据平台后台 - 商户支付参数配置，删除会员支付参数
     *
     * @param memberPayment 商户支付参数配置
     * @return 操作结果
     */
    @Transactional(rollbackFor = BusinessException.class)
    @Override
    public Wrapper<Void> removeMemberPaymentParameter(MemberPaymentDO memberPayment) {
        MemberPaymentParameterDO memberPaymentParameter = memberPaymentParameterRepository.findFirstByMemberIdAndRoleIdAndPayTypeAndPayChannel(memberPayment.getMemberId(), memberPayment.getRoleId(), memberPayment.getPayType(), memberPayment.getPayChannel());
        if(memberPaymentParameter == null) {
            return Wrapper.success();
        }

        memberPaymentParameter.getParameters().removeIf(p -> p.getCode().equals(memberPayment.getCode()));

        memberPaymentParameterRepository.saveAndFlush(memberPaymentParameter);
        return Wrapper.success();
    }
}
